//
// Created by Pulsar on 2020/5/18.
//
#include <optional>
#include <rc_network/rc_httpd_rest.h>
#include <rc_log/slog.hpp>
#include <iostream>
#include <rc_message/rc_msg_server.h>
#include <boost/thread/mutex.hpp>
#include <utility>
#include <rc_system/functions.h>
#include <rc_cv/MatConvert.h>
#include <rc_task/rcTaskVariable.h>

namespace rccore {
    namespace network {
        namespace httpd {
            void printCookies(const Http::Request &req) {
                auto cookies = req.cookies();
                std::cout << "Cookies: [" << std::endl;
                const std::string indent(4, ' ');
                for (const auto &c: cookies) {
                    std::cout << indent << c.name << " = " << c.value << std::endl;
                }
                hardware_concurrency();
                std::cout << "]" << std::endl;
            }

            typedef std::uint64_t hash_t;
            constexpr hash_t prime = 0x100000001b3ull;
            constexpr hash_t basis = 0xcbf29ce484222325ull;

            hash_t hash_(char const *str) {
                hash_t ret{basis};
                while (*str) {
                    ret ^= *str;
                    ret *= prime;
                    str++;
                }
                return ret;
            }

            constexpr hash_t hash_compile_time(char const *str, hash_t last_value = basis) {
                return *str ? hash_compile_time(str + 1, (*str ^ last_value) * prime) : last_value;
            }


            RcHttpdRest::RcHttpdRest(const std::shared_ptr<common::Context> &pcontext) : pcontext(pcontext) {
                Port serv_port(pcontext->pconfig->pconfigInfo->HTTPD_PORT);
                Address addr(Ipv4::any(), serv_port);
                httpEndpoint = std::make_shared<Http::Endpoint>(addr);
            }

            void RcHttpdRest::init(size_t thr) {
                auto opts = Http::Endpoint::options()
                        .threads(static_cast<int>(thr));
                httpEndpoint->init(opts);
                setupRoutes();
            }

            void RcHttpdRest::start() {
                httpEndpoint->setHandler(router.handler());
                httpEndpoint->serve();
            }

            void RcHttpdRest::setupRoutes() {
                using namespace Rest;
                Routes::Get(router, "/", Routes::bind(&RcHttpdRest::index, this));
                Routes::Get(router, "/index", Routes::bind(&RcHttpdRest::index, this));
                Routes::Get(router, "/js/:filename", Routes::bind(&RcHttpdRest::staticfile, this));
                Routes::Get(router, "/css/:filename", Routes::bind(&RcHttpdRest::staticfile, this));
                Routes::Get(router, "/image/:filename", Routes::bind(&RcHttpdRest::staticfile, this));
                Routes::Get(router, "/move/:action", Routes::bind(&RcHttpdRest::move, this));
                Routes::Get(router, "/video", Routes::bind(&RcHttpdRest::video, this));
                Routes::Get(router, "/settings", Routes::bind(&RcHttpdRest::settings, this));
                Routes::Post(router, "/settings", Routes::bind(&RcHttpdRest::settings, this));
                Routes::Get(router, "/stateinfo", Routes::bind(&RcHttpdRest::state_info, this));
            }

            void RcHttpdRest::state_info(const Rest::Request &request, Http::ResponseWriter response) {
                std::unique_lock<std::mutex> system_info_lock(task::task_variable::system_info_mutex);

                std::string local_date_time = functions::getTimeNow();
                local_date_time = "\"" + local_date_time + "\"";
                response.send(Http::Code::Ok,
                              "{\"datetime\":" + local_date_time + ","
                              + "\"IMU_ACC_X\":" + std::to_string(task::task_variable::systemInfo.IMU_ACC_X) + ","
                              + "\"IMU_ACC_Y\":" + std::to_string(task::task_variable::systemInfo.IMU_ACC_Y) + ","
                              + "\"IMU_ACC_Z\":" + std::to_string(task::task_variable::systemInfo.IMU_ACC_Z) + ","
                              + "\"IMU_GYRO_X\":" + std::to_string(task::task_variable::systemInfo.IMU_GYRO_X) + ","
                              + "\"IMU_GYRO_Y\":" + std::to_string(task::task_variable::systemInfo.IMU_GYRO_Y) + ","
                              + "\"IMU_GYRO_Z\":" + std::to_string(task::task_variable::systemInfo.IMU_GYRO_Z) + ","
                              + "\"IMU_ANGLE_X\":" + std::to_string(task::task_variable::systemInfo.IMU_ANGLE_X) + ","
                              + "\"IMU_ANGLE_Y\":" + std::to_string(task::task_variable::systemInfo.IMU_ANGLE_Y) + ","
                              + "\"IMU_ANGLE_Z\":" + std::to_string(task::task_variable::systemInfo.IMU_ANGLE_Z) + ","
                              + "\"IMU_MAG_X\":" + std::to_string(task::task_variable::systemInfo.IMU_MAG_X) + ","
                              + "\"IMU_MAG_Y\":" + std::to_string(task::task_variable::systemInfo.IMU_MAG_Y) + ","
                              + "\"IMU_MAG_Z\":" + std::to_string(task::task_variable::systemInfo.IMU_MAG_Z) + ","

                              + "\"EKF_ACTION_X_M_S\":" +
                              std::to_string(task::task_variable::systemInfo.EKF_ACTION_X_M_S) + ","
                              + "\"EKF_ACTION_Y_M_S\":" +
                              std::to_string(task::task_variable::systemInfo.EKF_ACTION_Y_M_S) + ","
                              + "\"EKF_ACTION_Z_M_S\":" +
                              std::to_string(task::task_variable::systemInfo.EKF_ACTION_Z_M_S) + ","
                              + "\"EKF_LAT\":" + std::to_string(task::task_variable::systemInfo.EKF_LAT) + ","
                              + "\"EKF_LNG\":" + std::to_string(task::task_variable::systemInfo.EKF_LNG) + ","
                              + "\"latitude_deg\":" + std::to_string(task::task_variable::systemInfo.latitude_deg) + ","
                              + "\"longitude_deg\":" + std::to_string(task::task_variable::systemInfo.longitude_deg) +
                              ","
                              + "\"EKF_TX\":" + std::to_string(task::task_variable::systemInfo.EKF_TX) + ","
                              + "\"EKF_TY\":" + std::to_string(task::task_variable::systemInfo.EKF_TY) + ","
                              + "\"EKF_TZ\":" + std::to_string(task::task_variable::systemInfo.EKF_TZ) + ","
                              + "\"TRANS_X\":" + std::to_string(task::task_variable::systemInfo.TRANS_X) + ","
                              + "\"TRANS_Y\":" + std::to_string(task::task_variable::systemInfo.TRANS_Y) + ","
                              + "\"TRANS_Z\":" + std::to_string(task::task_variable::systemInfo.TRANS_Z) + ","

                              + "\"EKF_EX\":" + std::to_string(task::task_variable::systemInfo.EKF_EX) + ","
                              + "\"EKF_EY\":" + std::to_string(task::task_variable::systemInfo.EKF_EY) + ","
                              + "\"EKF_EZ\":" + std::to_string(task::task_variable::systemInfo.EKF_EZ) + ","
                              + "\"EULAR_X\":" + std::to_string(task::task_variable::systemInfo.EULAR_X) + ","
                              + "\"EULAR_Y\":" + std::to_string(task::task_variable::systemInfo.EULAR_Y) + ","
                              + "\"EULAR_Z\":" + std::to_string(task::task_variable::systemInfo.EULAR_Z) + ","
                              + "\"DISTANCE\":" + std::to_string(task::task_variable::systemInfo.DISTANCE)
                              + "}");
                system_info_lock.unlock();
            }


            void RcHttpdRest::settings(const Rest::Request &request, Http::ResponseWriter response) {
                // TODO:输出系统信息
                time_t now = time(0);
                std::string local_date_time = functions::getTimeNow();
                local_date_time = "\"" + local_date_time + "\"";

                Pistache::Http::Method method = request.method();
                if (method == Pistache::Http::Method::Post) {

                } else if (method == Pistache::Http::Method::Get) {
                    response.send(Http::Code::Ok, "{\"datetime\":" + local_date_time + ","
                                                  + "\"AUTO_CONTROL\":" +
                                                  (pcontext->pconfig->pconfigInfo->AUTO_CONTROL ? "true" : "false") +
                                                  ","
                                                  + "\"EKF_ON\":" +
                                                  (pcontext->pconfig->pconfigInfo->EKF_ON ? "true" : "false") + ","
                                                  + R"("state":"ok")"
                                                  + "}"
                    );
                }

            }


            void RcHttpdRest::video(const Rest::Request &request, Http::ResponseWriter response) {
                if (not pcontext->pmessage_server->pimageMessage->empty()) {
                    std::string strRespon = pcontext->pmessage_server->pimageMessage->front_base64_string_message();
                    std::string local_date_time = functions::getTimeNow();
                    response.send(Http::Code::Ok, strRespon);
                } else {
                    std::string local_date_time = functions::getTimeNow();
                    response.send(Http::Code::Ok, R"({state:"error",message:"Image Queue Empty"})");
                }
            }

            void RcHttpdRest::index(const Rest::Request &request, Http::ResponseWriter response) {
                Http::serveFile(response, "html/index.html");
            }

            void RcHttpdRest::move(const Rest::Request &request, Http::ResponseWriter response) {
                auto action = request.param(":action").as<std::string>();
                std::string local_date_time = functions::getTimeNow();
                slog::info << _LOCALE_CLASSES_H << ":" << __FUNCTION__ << local_date_time << slog::endl;
                WHEEL_DATA wheelData = {0, 0, 0, 1, 1, 1};
                if (not pcontext->pconfig->pconfigInfo->AUTO_CONTROL) {
                    switch (hash_(action.c_str())) {
                        case hash_compile_time("ac"): {
                            response.send(Http::Code::Ok, "{\"datetime\":" + local_date_time + ","
                                                          + R"("state":"ok")" + ","
                                                          + "\"action\":" + "\"" + action + "\""
                                                          + "}");
                            break;
                        }
                    }
                }
                pcontext->pmessage_server->pmoveMessage->push_message(wheelData);
                response.send(Http::Code::Ok, action);
            }

            void RcHttpdRest::staticfile(const Rest::Request &request, Http::ResponseWriter response) {
                std::string res_path = "html/";
                auto filename = request.param(":filename").as<std::string>();
                std::string file_ext_name = filename.substr(filename.find_last_of('.') + 1);

                if (file_ext_name == "js") {
                    response.headers().add<Http::Header::ContentType>(MIME(Application, Javascript));
                    res_path = res_path + "js/" + filename;
                }
                if (file_ext_name == "css") {
                    response.headers().add<Http::Header::ContentType>(MIME(Text, Css));
                    res_path = res_path + "css/" + filename;
                }
                if (file_ext_name == "png" || file_ext_name == "gif" || file_ext_name == "jpeg") {
                    response.headers().add<Http::Header::ContentType>(MIME(Image, Jpeg));
                    res_path = res_path + "image/" + filename;
                }
                slog::wget << res_path << slog::endl;
                Http::serveFile(response, res_path);
            }


            void RcHttpdRest::stop() {
                httpEndpoint->shutdown();
            }
        }
    }
}